package com.zgx.plus.security.service;

import cn.hutool.core.util.StrUtil;
import com.zgx.plus.common.constant.RedisConstant;
import com.zgx.plus.common.enums.ExceptionEnum;
import com.zgx.plus.common.enums.UserStatus;
import com.zgx.plus.common.exception.BizException;
import com.zgx.plus.common.redis.RedisCache;
import com.zgx.plus.common.seucrity.AuthenticationContextHolder;
import com.zgx.plus.security.model.LoginUser;
import com.zgx.plus.system.api.pos.SysUser;
import com.zgx.plus.system.api.vos.SysUserVo;
import com.zgx.plus.system.service.SysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.text.MessageFormat;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 用户验证处理
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);

    @Value(value = "${user.password.maxRetryCount:5}")
    private int maxRetryCount;
    @Value(value = "${user.password.lockTime:10}")
    private int lockTime;

    @Autowired
    private RedisCache redisCache;
    @Autowired
    private SysUserService userService;
    @Autowired
    private SysPermissionService permissionService;
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUserVo user = userService.selectUserByUserName(username);
        if (Objects.isNull(user)) {
            log.info("登录用户：{} 不存在.", username);
            throw new BizException("登录用户：" + username + " 不存在");
        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
            log.info("登录用户：{} 已被删除.", username);
            throw new BizException("对不起，您的账号：" + username + " 已被删除");
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
            log.info("登录用户：{} 已被停用.", username);
            throw new BizException("对不起，您的账号：" + username + " 已停用");
        }

        Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
//        String username = usernamePasswordAuthenticationToken.getName();
        String password = usernamePasswordAuthenticationToken.getCredentials().toString();

        int retryCount = (int) Optional.ofNullable(redisCache.getCacheObject(getCacheKey(username))).orElse(0);
        if (retryCount >= maxRetryCount) {
//            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
//                    MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime)));
            throw new BizException(MessageFormat.format("重试已超过{0},账户被锁定{1}分钟", maxRetryCount, lockTime));
        }

        if (!bCryptPasswordEncoder.matches(password, user.getPassword())) {
            retryCount = retryCount + 1;
//            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
//                    MessageUtils.message("user.password.retry.limit.count", retryCount)));
            redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
            throw new BizException(ExceptionEnum.EXCEPTION_PWD);
        } else {
            clearLoginRecordCache(username);
        }

        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
    }


    /**
     * 登录账户密码错误次数缓存键名
     */
    private String getCacheKey(String username) {
        return StrUtil.concat(true, RedisConstant.REDIS_KEY_PWD_ERR_CNT, username);
    }

    public void clearLoginRecordCache(String loginName) {
        if (redisCache.hasKey(getCacheKey(loginName))) {
            redisCache.deleteObject(getCacheKey(loginName));
        }
    }
}
